/**
 * js拓展方法，提供常用方法
 * 采用lodash作为基础库
 * https://www.lodashjs.com/
 */
import cloneDeep from 'lodash/cloneDeep'
import pullAllWith from 'lodash/pullAllWith'
import isEqual from 'lodash/isEqual'
import isPlainObject from 'lodash/isPlainObject'
import merge from 'lodash/merge'

/**
 * 获取数据类型
 * @param {any} val 待判断的数据
 * @returns {string}
 */
export function dataType(val) {
  let dataType = typeof val
  if (dataType === 'object') {
    dataType = Array.isArray(val) ? 'array' : 'object'
  }
  return dataType
}
/**
 * 判断是否是指定的数据类型
 * @param {*} val 待判断的数据
 * @param {string} type 待判断的类型
 * @returns {boolean}
 */
export function isAssignType(val, type) {
  return dataType(val) === type
}

/**
 * 判断数据是否有效，即为null、undefined、NaN类型
 * @param {*} val 待判断的数据
 * @returns {boolean}
 */
export function isInvalid(val) {
  return (isAssignType(val, 'number') && isNaN(val)) || val === null || val === undefined
}

/**
 * 判断数据是否为空，即字符串为空、对象无key，数组为空，若为null、undefined、NaN则返回false。
 * 仅支持基本类型判断：string, number, array, object, boolean, function
 * @param {*} val 待判断的数据
 * @returns {boolean}
 */
export function isVoid(val) {
  const type = dataType(val)
  let res
  switch (type) {
    case 'string':
      res = val.trim() === ''
      break
    case 'object':
      res = isPlainObject(val) && Object.keys(val).length === 0
      break
    case 'array':
      res = val.length === 0
      break
    default:
      res = false
  }
  return res
}

/**
 * 判断数据是否有效，即不能为null、undefined、NaN类型，并且数据有值
 * @param {*} val 待判断的数据
 * @returns {boolean}
 */
export function isValid(val) {
  return !isInvalid(val) && !isVoid(val)
}

/**
 * 数组去重
 * @param {array} arr
 * @returns {array} 返回一个去重后的新数组
 */
export function uniqueArr(arr) {
  return Array.from(new Set(arr))
}

/**
 * 创建一个深拷贝的数组或对象
 * @param {array|object} source 待复制的数组或对象
 * @returns {array|object} 返回一个新的数组或对象
 */
export function deepClone(source) {
  return cloneDeep(source)
}

/**
 * 双数组去重
 * @param {array} arr1 原始数组
 * @param {array} arr2 待去重项
 * @returns 返回一个去重后的新数组
 */
export function diffArray(arr1, arr2) {
  const arr = cloneDeep(arr1)
  return pullAllWith(arr, arr2, isEqual)
}

/**
 * 将新对象合并到目标对象中，仅合并在目标对象中存在的key
 * @param {Object} target 目标对象
 * @param {Object} sources 来源对象
 * @param {Boolean} clone 是否返回一个新对象
 * @returns 返回一个新对象，新对象是来源对象的浅拷贝
 */
export function mergeObjectOnlyKey(target, sources, clone = true) {
  const result = {}
  Object.keys(target).forEach(key => {
    if (Object.hasOwnProperty.call(sources, key) && !isInvalid(sources[key])) {
      if (clone) {
        result[key] = sources[key]
      } else {
        target[key] = sources[key]
      }
    } else if (clone) {
      result[key] = target[key]
    }
  })
  return result
}

/**
 * 深度合并对象
 * @param {Object} target 目标对象
 * @param {} sources 来源对象
 * @returns 对目标对象进行修改
 */
export function mergeObject(target, sources) {
  merge(target, sources)
}

/**
 * 构造防抖
 * @param {Function} operate 目标任务
 * @param {number} delay 延时信息
 * @returns 闭包监听函数
 */
export function debounce(operate, delay) {
  let time = null
  let timer = null
  let newTime = null
  function task() {
    newTime = +new Date()
    if (newTime - time < delay) {
      timer = setTimeout(task, delay)
    } else {
      operate()
      timer = null
    }
    time = newTime
  }
  return function() {
    // 更新时间戳
    time = +new Date()
    if (!timer) {
      timer = setTimeout(task, delay)
    }
  }
}

/**
 * 将数组转换成对象
 * @param {array} arr 需要转换的数组
 * @param {string} key 如果是对象数组，则取每一项的key；如果是一般数组，则不填
 * @returns 返回一个新对象
 */
export function arrayToObject(arr, key) {
  const res = {}
  arr.forEach((item, index) => {
    let resKey
    if (typeof item === 'object') {
      resKey = item[key]
    } else {
      resKey = index
    }
    res[resKey] = item
  })
  return res
}

/**
 * 将对象数组转换成对象
 * @param {array} arr 需要转换的数组
 * @param {string} key 要取做key的字段
 * @param {string} value 要取做value的字段
 * @returns 返回一个新对象
 */
export function formatObjArrToObj(data, key, value) {
  const res = {}
  data.forEach(el => {
    const _key = el[key]
    res[_key] = el[value]
  })
  return res
}

export default {
  dataType,
  isAssignType,
  isInvalid,
  isValid,
  isVoid,
  uniqueArr,
  deepClone,
  diffArray,
  mergeObjectOnlyKey,
  mergeObject,
  arrayToObject,
  formatObjArrToObj
}
